//-------------------------------------------------------------------------------------------------
// <copyright file="Enable.cs" company="OfficeClip LLC">
// Copyright (c) OfficeClip LLC.  All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------------------

namespace OfficeClip.OpenSource.StyleFix
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using System.Xml;
    using System.Xml.XPath;

    public class Enable
    {
        private const string Namespace = @"http://schemas.microsoft.com/developer/msbuild/2003";
        private static string folderPath;
        /// <summary>
        /// Modifies the given project file to exclude all .cs files in the project.
        /// </summary>
        /// <param name="projectPath">The path to the project file.</param>
        public static string ModifyProjectFile(string projectPath, string filePath, bool IsExclude)
        {
            folderPath = Path.GetDirectoryName(projectPath);
            if ((File.GetAttributes(projectPath) & FileAttributes.ReadOnly) != 0)
            {
                return string.Format("Project is read-only: {0}", projectPath);
            }

            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(projectPath);

                XPathNavigator nav = doc.DocumentElement.CreateNavigator();

                nav.MoveToNext();
                nav.MoveToFirstChild();
                bool styleCopInImport = false;

                // Now go to the last node and add the stylecop entry so that it shows warning in the build
                while (nav.MoveToNext())
                {
                    if (
                        (nav.NodeType == XPathNodeType.Element) &&
                        (nav.Name == "Import"))
                    {
                       // Check if it is the stylecop exclusion line
                        nav.MoveToFirstAttribute();
                        if (nav.Value.Contains("StyleCop"))
                        {
                            styleCopInImport = true;
                            break;
                        }

                        nav.MoveToParent();
                    }
                }

                // Now go back to the last node and add the import project
                if (!styleCopInImport)
                {
                    nav.MoveToParent();
                    nav.MoveToParent();
                    nav.AppendChild(@"<Import Project=""$(ProgramFiles)\MSBuild\Microsoft\StyleCop\v4.3\Microsoft.StyleCop.targets"" />");
                }

                // Bring back to the same state
                nav = doc.DocumentElement.CreateNavigator();

                // Find first child called ItemGroup
                if (nav.MoveToChild("ItemGroup", Namespace))
                {
                    // Loop through all ItemGroups and add ExcludeFromSourceAnalysis tag to already existing Compile tags.
                    do
                    {
                        if (nav.Name == "ItemGroup")
                        {
                            // Add exclusion tags under this ItemGroup node.
                            ProcessItemGroupNode(nav, filePath, IsExclude);
                        }
                    }
                    while (nav.MoveToNext());

                    doc.Save(projectPath);
                }
            }
            catch (Exception ex)
            {
                return string.Format("Unable to update the file {0}: {1}", projectPath, ex.Message);
            }

            return string.Empty;
        }

        /// <summary>
        /// Goes through all the files and shows if the file is excluded from the stylecop
        /// </summary>
        /// <param name="hashExcluded">The key is the file path and the value is true if excluded from stylecop</param>
        /// <returns>Error message if error is found</returns>
        public static string GetStyleCopStatus(string projectPath, out Hashtable hashExcluded)
        {
            hashExcluded = new Hashtable();
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(projectPath);
                XPathNavigator nav = doc.DocumentElement.CreateNavigator();

                // Find first child called ItemGroup
                if (nav.MoveToChild("ItemGroup", Namespace))
                {
                    // Loop through all ItemGroups and add ExcludeFromSourceAnalysis tag to already existing Compile tags.
                    do
                    {
                        if (nav.Name == "ItemGroup")
                        {
                            // Add exclusion tags under this ItemGroup node.
                            // ProcessItemGroupNode(nav, filePath, IsExclude);
                            IsStyleCopDeactivated(nav, ref hashExcluded);
                        }
                    }
                    while (nav.MoveToNext());

                    // doc.Save(projectPath);
                }
            }
            catch (Exception e)
            {
                return e.Message;
            }

            return string.Empty;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="nav"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        private static void IsStyleCopDeactivated(XPathNavigator nav, ref Hashtable hashExcluded)
        {
            string fileName;
            if (nav.MoveToFirstChild())
            {
                do
                {
                    // nav.Name is about the Build Action for the file. For now it's only Compile files that
                    // are excluded from the StyleCop build process. None, Content and Embedded are the other options.
                    if (nav.Name == "Compile")
                    {
                        nav.MoveToFirstAttribute();
                        fileName = nav.Value;
                        hashExcluded[fileName.ToLower()] = false;
                        nav.MoveToParent();
                        if (nav.MoveToChild("ExcludeFromStyleCop", Namespace))
                        {
                            // The tag already exists.
                            string str = nav.Value;
                            hashExcluded[fileName.ToLower()] = (str == "true");
                            nav.MoveToParent();
                        }
                    }
                }
                    while (nav.MoveToNext());

                nav.MoveToParent();
            }
        }

        /// <summary>
        /// Adds exclusion tags to the given ItemGroup node.
        /// </summary>
        /// <param name="nav">The ItemGroup node.</param>
        private static void ProcessItemGroupNode(XPathNavigator nav, string filePath, bool isExclude)
        {
            string xmlFileName;
            if (nav.MoveToFirstChild())
            {
                do
                {
                    // nav.Name is about the Build Action for the file. For now it's only Compile files that
                    // are excluded from the StyleCop build process. None, Content and Embedded are the other options.
                    if (nav.Name == "Compile")
                    {
                        nav.MoveToFirstAttribute();
                        xmlFileName = folderPath + @"\" + nav.Value;
                        nav.MoveToParent();
                        if (xmlFileName.ToLower() != filePath.ToLower()) continue;
                        if (nav.MoveToChild("ExcludeFromStyleCop", Namespace))
                        {
                            // The tag already exists.
                            nav.SetValue((isExclude) ? "true" : "false");
                            nav.MoveToParent();
                        }
                        else
                        {
                            // The tag does not exist. Add it now.
                            nav.AppendChild(
                                string.Format(
                                    "<ExcludeFromStyleCop>{0}</ExcludeFromStyleCop>",
                                   (isExclude) ? "true" : "false"));
                        }
                    }
                }
                    while (nav.MoveToNext());

                nav.MoveToParent();
            }
        }

        // private static string ExcludeStyleCopTagValue(string filePath, bool isExclude)
        // {
        //    return (isExclude) ? "true" : "false";
        //    //if (xmlFileName == filePath)
        //    //{
        //    //    return (isExclude) ? "true" : "false";
        //    //}
        //    //return "false";
        // }
    }
}
